//  Copyright 2011 Google Inc. All Rights Reserved.
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

#library('isoserver');

#import('dart:io');
#import('dart:isolate');

class IsoServerHandler {
  final Path root;
  
  IsoServerHandler(this.root);
  
  void handleRequest(HttpRequest req, HttpResponse resp) {
    Path path = root.append("${req.path}.dart");
    print("Serving $path");
    File file = new File.fromPath(path);
    
    file.exists().then((bool exists) {
      if (exists) {
        SendPort sendPort = spawnUri("file:///$path");
        
        // explicitly create a ReceivePort so that isolates can send multiple
        // messages back to us.
        ReceivePort receivePort = new ReceivePort();
        
        // initial request message
        sendPort.send(_makeRequestMessage(req), receivePort.toSendPort());
        
        // listen for data on the input stream and send messages for each
        req.inputStream.onData = () {
          sendPort.send({
            'type': 'onData',
            'data': req.inputStream.read(),
            'available': req.inputStream.available()});
        };
        req.inputStream.onClosed = () {
          sendPort.send({'type': 'onClose'});
        };
        req.inputStream.onError = (e) {
          print("error: $e");
          sendPort.send({'type': 'onError', 'error': e});
        };
        
        
        // listen for incomming messages
        receivePort.receive((message, SendPort replyTo) {
          _dispatch(message, resp);
        });
      } else {
        resp.statusCode = HttpStatus.NOT_FOUND;
        resp.outputStream.close();
      }
    });
  }

  void _dispatch(Map<String, Object> message, HttpResponse resp) {
    switch (message['type']) {
      case 'writeString':
        resp.outputStream.writeString(message['string']);
        break;
      case 'close':
        resp.outputStream.close();
        break;
      case 'headers':
        // 'headers' mesage is sent the first time the response's OutputStream
        // is accessed. Headers can't be changed after that.
        Map map = message['headers'];
        map.forEach((k, v) {
          resp.headers.set(k, v);
        });
        break;
      default:
        print("unknown message: $message");
    }
  }
  
  Map<String, Object> _makeRequestMessage(HttpRequest req) {
    Map<String, List<String>> headers = new Map<String, List<String>>();
    req.headers.forEach((String name, List<String> values) {
      headers[name] = values;
    });
    return {
        'type': 'request',
        'contentLength' : req.contentLength,
        'cookies' : req.cookies,
        'headers': headers,
        'method': req.method,
        'path': req.path,
        'persistentConnection': req.persistentConnection,
        'protocolVersion': req.protocolVersion,
        'queryParameters': req.queryParameters,
        'queryString': req.queryString,
        'uri': req.uri,
        };
  }
}